package com.example.springsecurity.common.config;

import com.example.springsecurity.common.filter.JwtAuthenticationTokenFilter;
import com.example.springsecurity.common.filter.handle.RestAuthenticationEntryPoint;
import com.example.springsecurity.common.filter.handle.RestfulAccessDeniedHandler;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


/**
 * @Description：
 * @Author： fengcheng
 * @Date： 2024/12/16 17:33
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)  // 开启注解权限认证功能，配合@PreAuthorize注解使用，主要是对请求方法进行权限控制
public class SecurityConfig {

    @Value("${system.default.user.mobile}")
    private String mobile;

    @Value("${system.init.config.activated}")
    private String activated;

    @Resource
    private CorsConfig corsConfig;

    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Resource
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;

    @Resource
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    /**
     * 允许对于网站静态资源的无授权访问
     */
    public static final String[] URL_SOURCES = {
            "/swagger-ui/**",
            "/v3/api-docs/**",
            "/doc.html",
            "/webjars/**"
    };

    /**
     * 白名单不需要拦截
     */
    public static final String[] URL_WHITELIST = {
            "/system/login/login",             // 登陆接口
            "/system/login/logout",           // 登出接口
            "/system/login/enable",          // 是否获取验证码
            "/system/login/get",            // 获取验证码
            "/system/login/check",         // 检查验证码
            "/knife4j",                    // 接口文档地址
    };

    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // 由于使用的是JWT，我们这里不需要csrf
                .csrf(AbstractHttpConfigurer::disable)
                // 基于token，所以不需要session
                .sessionManagement(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(author -> author
                        // 允许对于网站静态资源的无授权访问
                        .requestMatchers(URL_SOURCES).permitAll()
                        // 允许匿名访问
                        .requestMatchers(URL_WHITELIST).permitAll()
                        .anyRequest().authenticated());
        // 禁用缓存
        http.headers(headers -> headers.cacheControl(HeadersConfigurer.CacheControlConfig::disable));

        // 在【UsernamePasswordAuthenticationFilter】之前添加JWT过滤器
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);


        // 添加自定义未授权和未登录结果返回
        http.exceptionHandling(exceptionHandling -> exceptionHandling
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint)
        );

        // 注入authenticationManager
        http.authenticationManager(authenticationManager);

        // 允许跨域请求
//        http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(corsConfig));
        return http.build();
    }

    /**
     * @Description：初始化租户、用户、角色、菜单等系统默认数据
     * @Author：fengcheng
     * @Date：2024/12/19 11:17
     * @Return：void
     */
    @PostConstruct
    public void initSystemDetailsInfo() throws InterruptedException {
        if (Boolean.parseBoolean(activated)){
            userDetailsService.createTenant();
            userDetailsService.createUser();
            userDetailsService.createRole();
            userDetailsService.createMenu();
            Thread.sleep(3000);
            userDetailsService.createUesrRole();
            userDetailsService.createRoleMenu();
            log.info("初始化租户、用户、角色、菜单等系统默认数据完成，默认登陆账号是：{}，密码是：123456", mobile);
        }
    }

    /**
     * @Description：初始化密码编码器
     * @Author：fengcheng
     * @Date：2024/12/18 11:41
     * @Return：org.springframework.security.crypto.password.PasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * @Description：初始化身份认证管理器
     * @Author：fengcheng
     * @Date：2024/12/19 11:07
     * @Return：org.springframework.security.authentication.AuthenticationManager
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(authenticationProvider);
    }
}
